use AppConfig qw(:expand :argcount);
use strict;

our %hdr;
our $article;
our %style_data_conf;
our $config;
our $body;
our %active;

my %returnvalues = (
			"article_max_crosspost"	=> 3,
			"article_max_followup"  => 4,
			"article_max_headsize"  => 5,
			"article_max_bodysize"  => 6,
			"article_max_totalsize" => 7,
		);


my @nomoderation = (    # Groups where 'Approved' header is required without being moderated                                                      
                        "fr.misc.bavardages.dinosaures",                      
                        "alt.sysadmin.recovery",                              
                        "alt.tech-support.recovery", 
                        "alt.dev.null",
                        "alt.hackers",
		   );

sub check_style($) {
	my $style = $_[0];			# Configuration settings
	my $mid = $article->mid();		# Message ID

	foreach (keys %style_data_conf)
	{
		if ($_ =~ /^article_/) {
		      my $flag 	= $_;
		      my $limit	= $style->get("$flag");	
		      my $artv	= $_;

		      $artv =~ s/^article_max_//;

		      my $value	= $article->get("$artv");
		
		      if ($limit > 0) {  # If check is enabled
			    if ($value > $limit) {   # if value exceed a maximum
				    &log("err", "Style Filter: Message $mid rejected: $flag is set to $limit, current value $value");
				    return $returnvalues{$flag};
			    } else {
				    &log("debug", "Style Filter: $flag = $value, maximum at $limit PASS") if ($config->verbose == 1);
			    }
		      } else {
			     &log("debug", "Style Filter: $flag disabled") if ($config->verbose == 1);
		      }
		 }
	   }

# Note: Control messages can include 'Control' or (if Control is used for another command) 'Also-Control' headers
#	both ones must be checked 

	if (( $hdr{'Control'} ne "" ) or ( $hdr{'Also-Control'} ne "" )) {
		&log("notice", "Style Filter: Message $mid includes a control header");
		if (($hdr{'Also-Control'} ne "") and ($style->allow_also_control != 1)) {
			&log("err", "Style Filter: Message $mid rejected: Also-Control is forbidden");
			return 8;
		}
		if (($hdr{'Control'} eq "") and ($hdr{'Also-Control'} ne "")) {
			&log("err", "Style Filter: Message $mid rejected: Control is empty, Also-Control has got a value ($hdr{'Also-Control'})");
			return 8;
		}
		if ($style->allow_all_control_message != 1) {
			if ($hdr{'Control'} ne "") {
			      if ($hdr{'Control'} =~ /^cancel/i) {
					if ($style->allow_control_cancel() == 0) {
						  &log("err", "Style Filter: Message $mid rejected: control cancels are forbidden");
						  return 8;
					}
			      } else {
					&log("err", "Style Filter: Message $mid rejected: Invalid control message $hdr{'Control'}");
					return 8;
			      }
			}
			
			if ($hdr{'Also-Control'} ne "") {
			      if ($hdr{'Also-Control'} =~ /^cancel/i) {
					if ($style->allow_control_cancel() == 0) {
						  &log("err", "Style Filter: Message $mid rejected: control cancels are forbidden");
						  return 8;
					}
			      } else {
					&log("err", "Style Filter: Message $mid rejected: Invalid control message $hdr{'Also-Control'}");
					return 8;
			      }
			}
		} else {
			&log("notice", "Style Filter: Control messages are accepted by default, skipping check");
		}
	}

	if ($style->allow_supersedes != 1) {
		if (($hdr{'Supersedes'} ne "") or ($hdr{'Cancel'} ne "") or ($hdr{'Replaces'} ne "")) {
			&log("err", "Style Filter: Message $mid rejected: Supersedes are not accepted");
			return 8;
		}
	}

# Note: Closed groups (close_group and close_followup setting) can be inside postfilter.conf or inside style.conf (per user)
#       so check must be done twice

	my $closedgroups = $style->banned_group();
	my $success = &check_closed_groups($closedgroups,$mid,$hdr{'Newsgroups'});
	return 9 if ($success != 0);

	$closedgroups = $config->banned_group();
	$success = &check_closed_groups($closedgroups,$mid,$hdr{'Newsgroups'});
	return 9 if ($success != 0);

	my $closedfup = $style->banned_followup();
	$success = &check_closed_groups($closedfup,$mid,$hdr{'Followup-To'});
	return 10 if ($success != 0);

	$closedfup = $config->banned_followup();
	$success = &check_closed_groups($closedfup,$mid,$hdr{'Followup-To'});
	return 10 if ($success != 0);

# Note: unless allow_external_followup being set to 1, each group inside Followup must be also inside Newsgroups

	if (($hdr{'Followup-To'} ne "") and ($hdr{'Newsgroups'} ne $hdr{'Followup-To'}) and ($hdr{'Followup-To'} !~ /^junk|^poster/i )) {
		  if ($style->allow_external_followup != 1) {
			my @groups = split(/\,/, $hdr{'Newsgroups'});
			my @fups   = split(/\,/, $hdr{'Followup-To'});

			foreach (@fups)
			{
				my $status = 0;
				my $grp = $_;
				foreach (@groups) {
				      if ($_ eq $grp) {
					    $status = 1;
				      }
				}
	                        if ($status == 0) {
	                                &log("err", "Style Filter: Message $mid rejected: followup was set to a group that is not inside Newsgroups");
        	                        return 11;
                	        }
			}
		  }
	}



# Note: some group requires a valid 'Approved' header even if it's unmoderated as a antispam measure.
#	groups which need this are stored in @nomoderation

	if ($hdr{'Approved'} ne "" ) {
		my $status = 0;
		foreach (@nomoderation) {
			if ( $hdr{'Newsgroups'} =~ /$_/i ) {
				&log("notice", "Style Filter: Message $mid was sent to $hdr{'Newsgroup'} which allows approved messages");
				$status = 1;
			}
		}
		if ($status == 0) {
				&log("err", "Style Filter: Message $mid rejected: found forbidden Approved header ($hdr{'Approved'})");
				return 12;
		}

	}

# Note: some spam message tries to look like a reply without being it
	if ( ($hdr{'Subject'} =~ /^Re:\ /i) and ($hdr{'References'} eq "")) {
                &log( "err",  "Style Filter: Message $mid rejected: Subject starts with \"Re:\" and there are no references" );
                return 13;
        }

# Note: some check about body
	

        if (($style->max_quoted_ratio != 0) and ($article->quoted_ratio >  $style->max_quoted_ratio)) {
                        &log("err", "Style Filter: Message $mid rejected: too many quoted lines");
                        return 14;
        }

	if (($style->max_empty_ratio != 0) and ($article->empty_ratio > $style->max_empty_ratio)) {
                        &log("err", "Style Filter: Message $mid rejected: too many empty lines");
                        return 15;
        }

	if (($style->max_blank_ratio != 0) and ($article->blank_ratio > $style->max_blank_ratio)) {
                        &log("err", "Style Filter: Message $mid rejected: too many blank lines");
                        return 16;
        }

	if (($style->max_email_ratio != 0) and ($article->email_ratio > $style->max_email_ratio)) {
                        &log("err", "Style Filter: Message $mid rejected: too many email addresses");
                        return 17;
        }

	if (($style->min_welldone_ratio != 0) and ($article->welldone_ratio < $style->min_welldone_ratio)) {
                        &log("err", "Style Filter: Message $mid rejected: too few valid lines");
                        return 18;
        }

	if (($style->max_bodyline_length != 0) and ($article->longest_line > $style->max_bodyline_length)) {
                        &log("err", "Style Filter: Message $mid rejected: too long body line");
                        return 19;
        }

	if (($style->allow_uuencoded_data == 0) and ($article->is_uuencoded() == 1)) {
                        &log("err", "Style Filter: Message $mid rejected: forbidden uuencoded data in the body");
                        return 20;
        }

	if (($style->allow_yenc_data == 0) and ($article->is_yenc() == 1)) {
                        &log("err", "Style Filter: Message $mid rejected: forbidden yenc data in the body");
                        return 21;
        }
# Date issues
	my $atime = $article->arrivaltime();
	my $dtime = $article->date();
	my $difftime = $atime - $dtime;
	if (($style->max_article_age != 0) and ($difftime > $style->max_article_age)) {
		&log("err", "Style Filter: Message $mid rejected: message too old");
                return 22;
        }
	if ($difftime < 0) {  # Message posted in the future (so it was received before being sent
		$difftime = $difftime * -1;	# Difference becomes positive
		if (($style->max_future_age != 0) and ($difftime > $style->max_future_age)) {
			&log("err", "Style Filter: Message $mid rejected: message posted in the future");
			return 23;
		}
	}

# active issues

	if (($style->allow_posting_on_virgin_groups == 0) or ($style->allow_posting_on_empty_groups == 0)) {
		my @groups = split(/\,/, $hdr{'Newsgroups'});
		foreach (@groups) {
			if (($active{$_} == -1) and ($style->allow_posting_on_virgin_groups == 0)) {
				 &log("err", "Style Filter: Message $mid rejected: $_ is a virgin group");
				 return 24;
			} elsif (($active{$_} == 0) and ($style->allow_posting_on_empty_groups == 0)) {
				 &log("err", "Style Filter: Message $mid rejected: $_ is an empty group");
				 return 25;
			}
		}
	}
	return 0;
}

sub check_closed_groups($$$) {
	my $closed_groups = $_[0];
	my $mid = $_[1];
	my $match = $_[2];

	foreach ( @$closed_groups ) {
		if ($match =~ /$_/i) {
			&log("err", "Style Filter: Message $mid rejected: Closed group $_ matches $match");
			return 1;
		} else {
			&log("debug", "Style Filter: Closed Groups: $match doesn't match $_") if ($config->verbose == 1);
		}
	}    
	return 0;
}


1;
